package com.beerandjesus.sportofkings.handicapper.strategy;

import com.beerandjesus.sportofkings.handicapper.model.MixedNumeral;
import org.apache.commons.lang.StringUtils;

import java.math.BigDecimal;

public class RacingCalculator {

    private static final String FURLONG = " Furlong";
    private static final String F = "f";
    private static final String MILE = " Mile";
    private static final String M = "M";
    private static final String FURLONGS = " Furlongs";
    private static final BigDecimal ZERO = new BigDecimal(0);
    public static final BigDecimal ONE_FURLONG_IN_YARDS = new BigDecimal(220);
    private static final int SEVEN_FURLONGS_IN_YARDS = 1540;
    private static final BigDecimal ONE_MILE_IN_YARDS = new BigDecimal(1760);

    public Float getFloatOrZero(String value) {
        try {
            return new Float(value.trim());
        } catch (Exception e) {
            return 0f;
        }
    }


    /**
     * Determine whether a given distance in yards is a sprint
     * or route
     *
     * @param yards
     * @return
     */
    public boolean isRoute(String yards) {
        BigDecimal dist;
        try {
            dist = new BigDecimal(yards);
        } catch (Exception e) {
            return false;
        }
        if (dist.abs().intValue() >= SEVEN_FURLONGS_IN_YARDS) {
            return true;
        }
        return false;
    }

    /**
     * Convert yards to furlongs or miles, displayed as a
     * fraction (e.g., 6 1/2 Furlongs).
     *
     * @param yards
     * @return The distance in furlongs or miles.
     */
    public String distanceFromYards(String yards) {
        return toFraction(yards, FURLONGS, MILE, FURLONG);

    }

    /**
     * Convert yards to furlongs or miles, displayed as a
     * fraction, using short notation (e.g., 6 1/2f).
     *
     * @param yards
     * @return
     */
    public String distanceFromYardsShortNotation(String yards) {
        return toFraction(yards, F, M, F);

    }

    private String toFraction(String yards, String furlongsMeasure,
                              String mileMeasure, String furlongMeasure) {
        if (StringUtils.isEmpty(yards)) {
            throw new IllegalArgumentException("String cannot be empty");
        }
        String measure = furlongsMeasure;
        MixedNumeral fraction;
        BigDecimal d = new BigDecimal(yards.trim()).abs();
        try {
            if (d.compareTo(ONE_MILE_IN_YARDS) >= 0) {
                // Miles
                BigDecimal div = d.divide(ONE_MILE_IN_YARDS);
                fraction = toFraction(div);
                measure = mileMeasure;
            } else {
                // Furlongs
                BigDecimal div = d.divide(ONE_FURLONG_IN_YARDS);
                fraction = toFraction(div);
                if (fraction.getInteger() <= 1) {
                    measure = furlongMeasure;
                }
            }
        } catch (ArithmeticException e) {
            return yards + " yards";
        }
        return fraction.toString() + measure;
    }

    /**
     * Convert a number to a fraction, for example, 1.0625 = 1 1/16.
     *
     * @param d The number to convert
     * @return A MixedNumeral for the decimal reduced to its
     * lowest common denomenator.
     */
    public MixedNumeral toFraction(BigDecimal d) {
        String str = d.toPlainString();
        String decimal = str.substring(str.indexOf(".") + 1, str.length());
        String bottom = "1";
        for (int j = 1; j <= decimal.length(); j++) {
            bottom += "0";
        }
        if (!str.contains(".")) {
            // No fraction
            return new MixedNumeral(d.intValue(), 0, 0);
        }
        BigDecimal[] frct = new BigDecimal[3];
        frct[0] = new BigDecimal(str.substring(0, str.indexOf(".")));
        frct[1] = new BigDecimal(decimal);
        frct[2] = new BigDecimal(bottom);
        BigDecimal divisor;
        BigDecimal[] resTop;
        BigDecimal[] resBot;
        for (int i = 9; i > 1; i--) {
            divisor = new BigDecimal(i);
            resTop = frct[1].divideAndRemainder(divisor);
            resBot = frct[2].divideAndRemainder(divisor);
            if (resTop[1].compareTo(ZERO) == 0 &&
                    resBot[1].compareTo(ZERO) == 0) {
                frct[1] = resTop[0];
                frct[2] = resBot[0];
                i = 10;
                continue;
            }
        }

        int numerator = frct[1].intValue();
        int denomenator = frct[2].intValue();
        if (numerator == 0 || denomenator == 0) {
            numerator = 0;
            denomenator = 0;
        }
        return new MixedNumeral(frct[0].intValue(), numerator, denomenator);
    }

}
